<?php
/**********************************************************************************
WIKINDX: Bibliographic Management system.
Copyright (C)

This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program;
if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

The WIKINDX Team 2007
sirfragalot@users.sourceforge.net
**********************************************************************************/
/*****
*	BIBTEXIMPORTSTAGE2: BibTeX STAGE2 import class
*
*	$Header: /cvsroot/wikindx/wikindx3/core/file/import/BIBTEXIMPORTSTAGE2.php,v 1.60 2007/10/21 17:04:39 sirfragalot Exp $
*****/
/*
* @author Mark Grimshaw
* @version 2
*/
class BIBTEXIMPORTSTAGE2
{
// Constructor
	function BIBTEXIMPORTSTAGE2($db, $vars)
	{
		$this->db = $db;
		$this->db->escapeString = TRUE;
		$this->vars = $vars;
		include_once("core/session/SESSION.php");
		$this->session = new SESSION();
		include_once("core/messages/MESSAGES.php");
		$this->messages = new MESSAGES();
		include_once("core/messages/ERRORS.php");
		$this->errors = new ERRORS();
		include_once("core/messages/SUCCESS.php");
		$this->success = new SUCCESS();
// Load config arrays
		include_once("core/file/BIBTEXCONFIG.php");
		$this->config = new BIBTEXCONFIG();
		$this->config->bibtex();
		include_once("core/file/BIBTEXMAP.php");
		$this->map = new BIBTEXMAP();
// need to use English constants for BibTeX
		include_once("languages/en/CONSTANTS.php");
		$constants = new CONSTANTS_en();
		$constants->convertNumbers();
		$this->editionNumbers = array_flip($constants->cardinalToOrdinalWord());
		include_once("core/html/MISC.php");
		include_once("core/html/HTML.php");
		$this->html = new HTML();
		include_once("core/html/FORM.php");
		include_once("core/html/TABLE.php");
		include_once("core/file/TAG.php");
		$this->tag = new TAG($this->db);
		include_once("core/file/import/BIBTEXCREATORPARSE.php");
		$this->parseCreator = new BIBTEXCREATORPARSE();
		$this->parseCreator->separateInitials = TRUE;
		include_once("core/file/import/BIBTEXMONTHPARSE.php");
		$this->monthObj = new BIBTEXMONTHPARSE();
		include_once("core/file/import/BIBTEXPAGEPARSE.php");
		$this->pages = new BIBTEXPAGEPARSE();
		include_once("core/file/import/IMPORTCOMMON.php");
		$this->common = new IMPORTCOMMON($this->db);
		$this->resourceAdded = $this->resourceDiscarded = 
			$this->resourceAddedThisRound = $this->resourceDiscardedThisRound = 0;
// For an import from a plug-in like ImportPubMed, this variable will be a file path
		$this->importFile = FALSE;
		$this->oldTime = time();
	}
/*
* init - start the process
* 
* @author Mark Grimshaw
* @param string $paste	If TRUE, this is an ordinary user cut 'n' pasting bibtex entries into a textarea box.
* @param string $message - optional error message
* @return string
*/
	function init($paste, $message = FALSE)
	{
// if session variable 'bibtexImportLock' is TRUE, user is simply reloading this form
		if($this->session->getVar('importLock'))
			$this->badInput($this->errors->text("done", "fileImport"));
		$this->paste = $paste;
		$this->fileName = $this->gatherStage1();
		$this->rejectTitles = array();
		if(!$this->session->getVar("import_unrecognisedFields"))
		{
			include_once("core/file/import/BIBTEXPARSE.php");
			$parse = new BIBTEXPARSE();
			$parse->expandMacro = TRUE; // substitute @string values
			$parse->openBib($this->fileName);
			$parse->extractEntries();
			$parse->closeBib();
			list($null, $this->strings, $entries, $null2) = $parse->returnArrays(); // don't need preamble
		}
		else
		{
			list($this->vars, $this->strings, $this->customFields, $entries, $this->unrecognisedFields) = 
				$this->common->stage3($this->vars, $this);
		}
		if(empty($entries))
		{
			$pString = MISC::p($this->messages->text("import", "empty"));
			$this->session->setVar('importLock', TRUE);
			return $pString;
		}
		if(!$this->session->getVar("import_unrecognisedFields"))
		{
			$pString = '';
			if($this->paste)
				$pString .= FORM::hidden('import_paste', $this->vars['import_paste']);
			if($fields = $this->findInvalidFields($entries))
				return $pString . $fields;
		}
// NB - we need to write data to database as UTF-8 and parse all bibTeX values for laTeX code
		$this->entriesLeft = $this->entries = $this->convertEntries($entries);
		$finalInput = $this->writeDb();
		return $this->cleanUp($finalInput);
	}
/**
* Continue an import
*
*/
	function continueImport()
	{
		if($this->session->issetVar("import_rejectTitles"))
			$this->rejectTitles = unserialize(base64_decode($this->session->getVar("import_rejectTitles")));
		else
			$this->rejectTitles = array();
// Restore session
// Number added so far
		$this->resourceAdded = $this->session->getVar("import_resourceAdded");
// Number discarded so far
		$this->resourceDiscarded = $this->session->getVar("import_resourceDiscarded");
// tag ID
		if($this->session->issetVar("import_tagID"))
			$this->tagId = $this->session->getVar("import_tagID");
// bibtexString ID
		if($this->session->issetVar("import_bibtexStringID"))
			$this->bibtexStringId = $this->session->getVar("import_bibtexStringID");
		$this->entriesLeft = $this->entries = 
			unserialize(base64_decode($this->session->getVar("import_entries")));
		if($this->session->issetVar("import_unrecognisedFields"))
		{
			$this->unrecognisedFields = 
				unserialize(base64_decode($this->session->getVar("import_unrecognisedFields")));
			$this->customFields = unserialize(base64_decode($this->session->getVar("import_customFields")));
			$this->vars = unserialize(base64_decode($this->session->getVar("import_thisVars")));
		}
		$finalInput = $this->writeDb(TRUE);
		return $this->cleanUp($finalInput);
	}
/**
* Garbage clean up and intermediate session saving when importing in chunks
*
*/
	function cleanUp($finalInput)
	{
// update total no. resources in summary table
		$recordset = $this->db->select(array('WKX_database_summary'), array("totalResources"));
		$totalResources = $this->db->fetchOne($recordset) + $this->resourceAddedThisRound;
		$this->db->update('WKX_database_summary', array("totalResources" => $totalResources));
		if($finalInput)
		{
			@unlink($this->fileName); // remove garbage - ignore errors
			$pString = MISC::p($this->success->text("bibtexImport"));
			$pString .= MISC::p($this->messages->text("import", "added", " " . $this->resourceAdded));
			$pString .= $this->common->printDuplicates($this->resourceDiscarded, $this->rejectTitles);
			$this->session->setVar('importLock', TRUE);
		}
		else
		{
// Store temporary session variables
// Number added
			$this->session->setVar("import_resourceAdded", $this->resourceAdded);
// Number of rejects
			$this->session->setVar("import_resourceDiscarded", $this->resourceDiscarded);
// tag ID
			if(isset($this->tagId))
				$this->session->setVar("import_tagID", $this->tagId);
// bibtexString ID
			if(isset($this->bibtexStringId))
				$this->session->setVar("import_bibtexStringID", $this->bibtexStringId);
// Remaining entries
			$this->session->setVar("import_entries", base64_encode(serialize($this->entriesLeft)));
// Rejected titles
			if(!empty($this->rejectTitles))
				$this->session->setVar("import_rejectTitles", base64_encode(serialize($this->rejectTitles)));
// Unrecognised field mapping
			if(isset($this->unrecognisedFields))
			{
				$this->session->setVar("import_unrecognisedFields", 
					base64_encode(serialize($this->unrecognisedFields)));
// Custom field mapping
				if(isset($this->customFields))
					$this->session->setVar("import_customFields", base64_encode(serialize($this->customFields)));
// $this->vars
				$this->session->setVar("import_thisVars", 
					base64_encode(serialize($this->vars)));
			}
			$remainder = sizeof($this->entriesLeft);
			$pString = MISC::p($this->messages->text("import", "executionTimeExceeded", 
				ini_get("max_execution_time")));
			$pString .= MISC::p($this->messages->text("import", "addedChunk", " " . 
				$this->resourceAddedThisRound));
			$pString .= MISC::p("$remainder entries remaining.");
			$pString .= FORM::formHeader("importBibtexStage3Continue");
			$pString .= MISC::p(FORM::formSubmit("Continue"));
			$pString .= FORM::formEnd();
		}
		return $pString;
	}
/**
* find unrecognised field names
* 
* @author Mark Grimshaw
*/
	function findInvalidFields($entries)
	{
		$this->invalidFieldNames = $inputTypes = array();
		$this->day = $this->month = FALSE;
		foreach($entries as $entry)
		{
			list($this->url, $null) = $this->grabHowPublished($entry);
			$type = $this->getType($entry);
			foreach($entry as $field => $value)
			{
				if($field == 'bibtexEntryType')
				{
					$inputTypes[] = $value;
					continue;
				}
				if($field == 'bibtexCitation')
					continue;
				if((array_search($field, $this->map->validFields) === FALSE) && 
					(array_search($field, $this->invalidFieldNames) === FALSE))
					$this->invalidFieldNames[] = $field;
			}
		}
		if(!empty($this->invalidFieldNames)) // prompt to map field names
			return $this->common->promptFieldNames($entries, $inputTypes, $this->vars, $this->map, 
				$this->invalidFieldNames, $this->strings, $this);
		return FALSE; // continue with import.
	}
/*
* writeDb - write input to the database.
* 
* @author Mark Grimshaw
*/
	function writeDb($continue = FALSE)
	{
		$tagWritten = $stringWritten = FALSE;
		if(!$continue)
			$this->tagId = FALSE;
		if($this->session->getVar("setup_superadmin") || $this->session->getVar("setup_importBib"))
			$pasteLimit = FALSE;
		else
			$pasteLimit = TRUE;
		$finalInput = TRUE;
		foreach($this->entries as $key => $entry)
		{
			unset($this->entriesLeft[$key]);
			$authors = $editors = $this->collectionStringArray = array();
// For a user cut 'n' pasting. Admin is unlimited.
			if($pasteLimit && ($this->resourceAdded >= $this->session->getVar("setup_maxPaste")))
				break;
			$this->keywords = $this->note = $this->abstract = $this->authorList = 
				$this->editorList = $this->url = $this->month = $this->day = $this->confPublisherId = 
				$this->thesisType = $this->publisherId = $this->collectionId = FALSE;
			$wkType = $this->getType($entry);
			if(($authorWkField = array_search('author', $this->map->{$wkType}['resource_creator'])) && 
				array_key_exists('author', $entry) && $entry['author'])
				$authors = $this->parseCreator->parse($entry['author']);
// For duplicate checking, we try to find the surname of the primary creator ($authors[0][2] is the surname)
			if(!empty($authors) && array_key_exists(0, $authors) && $authors[0][2])
				$creatorSurname = $authors[0][2];
			else
				$creatorSurname = FALSE;
// ignore wikindx resource type book_chapter when checking duplicates
			if(($wkType != 'book_chapter') && !$this->session->getVar('import_importDuplicates') 
				&& array_key_exists('title', $entry) && 
				$this->common->titleExists($entry['title'], trim($creatorSurname), $wkType))
			{
				$rejectTitle = $entry['title'] . ".";
				if(array_key_exists('author', $entry) && $entry['author'])
					$rejectTitle = trim($entry['author']) . " " . $rejectTitle;
				$this->rejectTitles[] = $rejectTitle;
				$this->resourceDiscarded++;
				$this->resourceDiscardedThisRound++;
				continue;
			}
// bibTeX's 'article' type can be wikindx's journal_article, magazine_article or newspaper_article.  If there is no 'month' field, 
// we assume the first, if there's 'month' but no day part of that field, we assume the second and, if there's a day part, assume 
// the third. So, before we can write the resource table and its `type` field, we need to query any month field in the import.
			list($this->startMonth, $this->startDay, $this->endMonth, $this->endDay) = $this->grabMonth($entry);
// A bibtex type with a howpublished field containing a URL is mapped to wikindx's web_article type.
			list($this->url, $this->howPublished) = $this->grabHowPublished($entry);
			list($this->rejected, $entry, $custom) = $this->reject($entry, $wkType);
//print_r($rejected); print "<P>";
			$this->resourceId = $this->writeResourceTable($entry, $wkType);
			$addedResourceIds[] = $this->resourceId;
// add any import tag and get tag auto ID.  We write it here after the resource table in case we forbid duplicates and all 
// bibtex entries are duplicates - we don't want an empty tag in the WKX_tag table.
			if(!$continue)
			{
				if(!$tagWritten)
				{
					$this->tagId = $this->writeTagTable();
					$tagWritten = TRUE;
				}
				if(!$stringWritten)
				{
					$this->writeBibtexStringTable();
					$stringWritten = TRUE;
				}
			}
			if(!empty($authors))
				$this->authorList = $this->writeCreatorTable($authors);
			if(($editorWkField = array_search('editor', $this->map->{$wkType}['resource_creator'])) && 
				array_key_exists('editor', $entry) && $entry['editor'])
			{
				$editors = $this->parseCreator->parse($entry['editor']);
				$this->editorList = $this->writeCreatorTable($editors);
			}
			if($authorWkField || $editorWkField)
				$this->writeResourceCreatorTable($authorWkField, $editorWkField);
			$this->writePublisherTable($entry, $wkType);
			$this->writeResourceMiscTable($entry, $wkType);
			$this->writeResourceYearTable($entry, $wkType);
			$this->writeResourcePageTable($entry);
			$this->writeResourceNoteTable($entry);
			$this->writeResourceAbstractTable($entry);
			$this->writeResourceKeywordTable($entry);
			$this->writeResourceCustomTable($custom);
			$this->writeResourceCategoryTable();
			$this->writeCollectionTable($entry, $wkType);
			$this->writeResourceTimestampTable();
			$this->writeImportRawTable();
			$this->writeBibtexKey();
			$this->resourceAdded++;
			$this->resourceAddedThisRound++;
// Check we have more than 4 seconds buffer before max_execution_time times out.
			if((time() - $this->oldTime) >= (ini_get("max_execution_time") - 4))
			{
				$finalInput = FALSE;
				break;
			}
		}
// email notification
		if(isset($addedResourceIds))
		{
			$this->common->bibliographyAdd($addedResourceIds);
			$this->common->notify($addedResourceIds);
		}
		return $finalInput;
	}

/*
* reject -- gather rejected fields that wikindx does not recognise for that type and remove from $entry
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @return $wkType - the WIKINDX resource type for this bibtex entry
* @return $rejected - array of rejected field and their values (with bibTeX delimiters added back in)
* @return $newEntry - $entry with $rejected elements removed
*/
	function reject($entry, $wkType)
	{
		$rejectedEntry = FALSE;
		$index = 0;
		$custom = array();
		foreach($entry as $key => $value)
		{
			if(($key == 'bibtexEntryType') || 
			($key == 'howpublished') || ($key == 'abstract') || ($key == 'keywords'))
			{
				$newEntry[$key] = $value;
				continue;
			}
			if($key == 'bibtexCitation')
			{
				$rejected['citation'] = trim($value);
				$rejectedEntry = TRUE;
				continue;
			}
			if($key == 'note') // Use 'note' in preference to 'annote'
			{
				$newEntry[$key] = $value;
				continue;
			}
			if(($key == 'annote') && !array_key_exists('note', $entry)) // Use 'note' in preference to 'annote'
			{
				$newEntry[$key] = $value;
				continue;
			}
			if(($key == 'month') && $this->url && ($wkType == 'web_article'))
				continue;
			if(array_search($key, $this->map->{$wkType}['possible']) !== FALSE)
			{
				if(!array_key_exists($key, $newEntry) || 
					!array_key_exists('import_precedence', $this->vars))
						$newEntry[$key] = $value;
			}
// Do we map unrecognised fields?
			if(isset($this->unrecognisedFields) && array_search($key, $this->unrecognisedFields) !== FALSE)
			{
				$importKey = 'import_' . $key;
				if(array_key_exists($importKey, $this->vars) && 
					array_search($this->vars[$importKey], $this->map->{$wkType}['possible']) !== FALSE)
				{
// Do unrecognised fields take precedence?
					if(array_key_exists('import_precedence', $this->vars))
					{
						$newEntry[$this->vars[$importKey]] = $value;
						continue;
					}
					if(!array_key_exists($this->vars[$importKey], $newEntry))
					{
						$newEntry[$this->vars[$importKey]] = $value;
						continue;
					}
				}
			}
			if(array_key_exists($key, $newEntry))
				continue;
			if(isset($this->customFields) && array_key_exists($key, $this->customFields))
			{
				$custom[$key] = $value;
				continue;
			}
// If we get here, we have a bibtex field and value that are not recognised by wikindx. If this is not to be mapped, we need to to store this in case user 
// has requested that unused fields are also stored in the database.
// Return any @STRING substitution in $value back to original state
			$rejectedEntry = TRUE;
// Do @string substitutions
			if(!empty($this->strings) && ($strKey = array_search($value, $this->strings)))
				$rejected[$key] = $strKey;
// No substitution so return quoted
			else
				$rejected[$key] = "\"" . $value . "\"";
		}
		if(!$rejectedEntry)
			return array(FALSE, $newEntry, $custom);
		return array($rejected, $newEntry, $custom);
	}
/*
* getType - figure out what wikindx type this entry is
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @return $wkType - the WIKINDX resource type for this bibtex entry
*/
	function getType($entry)
	{
		if($entry['bibtexEntryType'] == 'article')
		{
			if($this->day)
				$wkType = 'newspaper_article';
			else if($this->month)
				$wkType = 'magazine_article';
			else // no day or month
				$wkType = 'journal_article';
		}
		else if(($entry['bibtexEntryType'] == 'misc') && $this->url)
			$wkType = 'web_article';
		else if($entry['bibtexEntryType'] == 'misc')
			$wkType = 'miscellaneous';
		else if($entry['bibtexEntryType'] == 'book')
			$wkType = 'book';
		else if($entry['bibtexEntryType'] == 'techreport')
			$wkType = 'report';
		else if($entry['bibtexEntryType'] == 'patent')
			$wkType = 'patent';
		else if($entry['bibtexEntryType'] == 'unpublished')
			$wkType = 'unpublished';
		else if($entry['bibtexEntryType'] == 'mastersthesis')
		{
			$wkType = 'thesis';
			$this->thesisType = "master's";
		}
		else if($entry['bibtexEntryType'] == 'phdthesis')
		{
			$wkType = 'thesis';
			$this->thesisType = "PhD";
		}
		else if(($entry['bibtexEntryType'] == 'conference') ||
			($entry['bibtexEntryType'] == 'inproceedings'))
			$wkType = 'proceedings_article';
// inbook type with a chapter field that is numeric, bibtex field 'chapter' is wikindx's title, bibtex field 'title' is wikindx collectionTitle
		else if(($entry['bibtexEntryType'] == 'inbook') && array_key_exists('chapter', $entry) &&
			is_numeric($entry['chapter']))
			$wkType = 'book_chapter';
// incorrect bibtex but we allow it anyhow making it a wikindx book_article type
		else if(($entry['bibtexEntryType'] == 'inbook') && array_key_exists('chapter', $entry))
			$wkType = 'book_article';
		else if(($entry['bibtexEntryType'] == 'incollection') || 
			($entry['bibtexEntryType'] == 'inbook'))
			$wkType = 'book_article';
		else if(($entry['bibtexEntryType'] == 'collection') ||
			($entry['bibtexEntryType'] == 'proceedings'))
			$wkType = 'proceedings';
		else if(!$wkType = array_search($entry['bibtexEntryType'], $this->map->types))
			$wkType = 'miscellaneous'; // everything else
		return $wkType;
	}
/*
* writeResourceTable - write WKX_resource table and get lastAutoId
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @param $wkType - the WIKINDX resource type for this bibtex entry
* @return autoId
*/
	function writeResourceTable($entry, $wkType)
	{
// bibTeX has no way of saying whether a thesis is a thesis or a dissertation so here we force it to 'thesis'.
		if($wkType == 'thesis')
		{
			$fields[] = 'field2';
			$values[] = 'thesis';
		}
		$fields[] = 'type';
		$values[] = $wkType;
// inbook type with a chapter field that is numeric, bibtex field 'chapter' is wikindx's title, bibtex field 'title' is wikindx collectionTitle
		if($wkType == 'book_chapter')
		{
			$title = $entry['chapter'];
			$noSort = $subtitle = '';
		}
// This was originally bibtex @inbook, because the bibtex field chapter was nonnumeric, it's been converted to wikindx book_article.
// bibtex field 'chapter' is wikindx's title, bibtex field 'title' is wikindx collectionTitle
		else if(($wkType == 'book_article') && array_key_exists('chapter', $entry))
			list($noSort, $title, $subtitle) = $this->common->splitTitle($entry['chapter']);
		else if(array_key_exists('title', $entry))
			list($noSort, $title, $subtitle) = $this->common->splitTitle($entry['title']);
		else
			$noSort = $title = $subtitle = '';
		$fields[] = 'title';
		$values[] = $title;
		if($noSort)
		{
			$fields[] = 'noSort';
			$values[] = $noSort;
		}
		if($subtitle)
		{
			$fields[] = 'subtitle';
			$values[] = $subtitle;
		}
		if($this->thesisType)
		{
			$fields[] = 'field1';
			$values[] = $this->thesisType;
		}
		if(($wkType == 'miscellaneous') && $this->howPublished)
		{
			$fields[] = $this->map->miscellaneous['howpublished'];
			$values[] = $this->howPublished;
		}
		if($this->url) // from @misc 'howpublished' field
			$entry['url'] = $this->url;
		foreach($entry as $bibField => $bibValue)
		{
// ISBN, ISSN and URL are uppercase in BIBTEXMAP but everything in $entry is lowercase
			if(($bibField == 'url') || ($bibField == 'isbn') || ($bibField == 'issn') || ($bibField == 'doi'))
				$bibField = strtoupper($bibField);
			if($wkField = array_search($bibField, $this->map->{$wkType}['resource']))
			{
				$fields[] = $wkField;
				$values[] = $bibValue;
				$this->collectionStringArray[$wkField] = $bibValue;
			}
		}
		$this->db->insert('WKX_resource', $fields, $values);
		return $this->db->lastAutoID('WKX_resource');
	}
/*
* writeCreatorTable - write WKX_creator table and get lastAutoId
* @author Mark Grimshaw
*
* @param $creators - assoc array of creators for one entry
* @return comma-separated list of creator IDs ready for insertion into WKX_resource_creator table.
*/
	function writeCreatorTable($creators)
	{
		if(!$creators)
			return FALSE;
		foreach($creators as $array)
		{
			$prefix = trim($array[4]);
			$surname = trim($array[2]) . ' ' . trim($array[3]);
			$firstname = trim($array[0]);
			$initials = str_replace('.', '', trim($array[1]));
			$creatorIds[] = $this->common->writeCreatorTable($firstname, $initials, $prefix, $surname);
		}
		return implode(',', $creatorIds);
	}
/*
* writeResourceCreatorTable - write to WKX_resource_creator table
* @author Mark Grimshaw
*
* @param $authorWkField - wikindx equivalent field in WKX_resource_creator for a bibtex 'author' field.
* @param $editorWkField - wikindx equivalent field in WKX_resource_creator for a bibtex 'editor' field.
*/
	function writeResourceCreatorTable($authorWkField, $editorWkField)
	{
		if($authorWkField && $this->authorList)
		{
			$fields[] = $authorWkField;
			$values[] = $this->authorList;
		}
		if($editorWkField && $this->editorList)
		{
			$fields[] = $editorWkField;
			$values[] = $this->collectionStringArray["creator2"] = $this->editorList;
		}
		if(!isset($fields))
			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$this->db->insert('WKX_resource_creator', $fields, $values);
	}
/*
* writeCollectionTable - write WKX_collection table
* @author Mark Grimshaw
*
* The only input from bibtex that can be a wikindx 'collection' is the 'journal' field or, for @inbook, 
* the 'booktitle' field.
*
* @param $entry - assoc array of one entry for import.
* @param $wkType - the WIKINDX resource type for this bibtex entry
*/
	function writeCollectionTable($entry, $wkType)
	{
		$title = FALSE;
// inbook type with a chapter field that is numeric, bibtex field 'chapter' is wikindx's title, bibtex field 'title' is wikindx collectionTitle
		if($wkType == 'book_chapter')
			$title = trim($entry['title']);
// This was originally bibtex @inbook, because the bibtex field chapter was nonnumeric, it's been converted to wikindx book_article.
// bibtex field 'chapter' is wikindx's title, bibtex field 'title' is wikindx collectionTitle
		else if(($wkType == 'book_article') && array_key_exists('chapter', $entry))
			$title = trim($entry['title']);
		else if(($wkType == 'book_article') && !array_key_exists('booktitle', $entry))
			return;
		else if(array_key_exists('booktitle', $entry))
			$title = trim($entry['booktitle']);
		if(!$title && !array_key_exists('journal', $entry))
			return;
		else if(!$title && array_key_exists('journal', $entry))
			$title = trim($entry['journal']);
		if(!$title)
			return;
		if(!empty($this->strings))
			$short = array_search($title, $this->strings);
		else
			$short = FALSE;
		$this->collectionStringArray['type'] = $wkType;
		$this->collectionStringArray['collection'] = $title;
		$this->collectionStringArray['collectionShort'] = $short;
		$this->collectionId = $this->common->writeCollectionTable($this->collectionStringArray, 
			$this->publisherId, $this->confPublisherId);
// update this resource_misc table row with the collectionId
		$this->db->update('WKX_resource_misc', array('collection' => $this->collectionId), 
			' WHERE ' . $this->db->formatField('id') . '=' . $this->db->tidyInput($this->resourceId));
	}
/*
* writePublisherTable - write WKX_publisher table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @param $wkType - the WIKINDX resource type for this bibtex entry
*/
	function writePublisherTable($entry, $wkType)
	{
		$organization = $publisherName = $publisherLocation = $conferenceLocation = FALSE;
		if(array_key_exists('publisher', $entry))
			$publisherName = trim($entry['publisher']);
		if(array_key_exists('organization', $entry) && ($wkType != 'proceedings_article'))
			$publisherName = trim($entry['organization']);
		else if(array_key_exists('organization', $entry))
			$organization = trim($entry['organization']);
		else if(array_key_exists('school', $entry))
			$publisherName = trim($entry['school']);
		else if(array_key_exists('institution', $entry))
			$publisherName = trim($entry['institution']);
		if(!$organization && !$publisherName)
			return;
		if(array_key_exists('address', $entry) && ($wkType != 'proceedings_article'))
			$publisherLocation = trim($entry['address']);
		else if(array_key_exists('address', $entry))
			$conferenceLocation = trim($entry['address']);
		if(array_key_exists('location', $entry))
		{
			if($wkType == 'proceedings_article')
				$conferenceLocation = trim($entry['location']);
			else
				$publisherLocation = trim($entry['location']);
		}
		if($wkType == 'proceedings_article')
		{
			$this->publisherId = 
				$this->common->writePublisherTable($organization, $conferenceLocation, $wkType);
			$this->confPublisherId = 
				$this->common->writePublisherTable($publisherName, $publisherLocation, $wkType);
		}
		else
			$this->publisherId = 
				$this->common->writePublisherTable($publisherName, $publisherLocation, $wkType);
	}
/*
* writeResourceMiscTable - write WKX_resource_misc table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @param $wkType - the WIKINDX resource type for this bibtex entry
*/
	function writeResourceMiscTable($entry, $wkType)
	{
		foreach($entry as $bibField => $bibValue)
		{
			if($wkField = array_search($bibField, $this->map->{$wkType}['resource_misc']))
			{
				$fields[] = $wkField;
				$values[] = $bibValue;
				$this->collectionStringArray[$wkField] = $bibValue;
			}
		}
		if($this->collectionId)
		{
			$fields[] = 'collection';
			$values[] = $this->collectionId;
		}
		if($this->publisherId)
		{
			$fields[] = 'publisher';
			$values[] = $this->publisherId;
		}
		if($this->confPublisherId)
		{
			$fields[] = "miscField1";
			$values[] = $this->confPublisherId;
		}
		if($this->tagId)
		{
			$fields[] = 'tag';
			$values[] = $this->tagId;
		}
		if(($wkType == 'newspaper_article') || ($wkType == 'magazine_article') || 
			($wkType == 'proceedings_article') || ($wkType == 'proceedings') || 
			($wkType == 'journal_article') || ($this->url && ($wkType == 'web_article')))
		{
			if($this->startMonth)
			{
				$fields[] = 'miscField3';
				$values[] = $this->startMonth;
				$this->collectionStringArray['miscField3'] = $this->startMonth;
			}
			if($this->startDay)
			{
				$fields[] = 'miscField2';
				$values[] = $this->startDay;
				$this->collectionStringArray['miscField2'] = $this->startDay;
			}
		}
		if(($wkType == 'proceedings_article') || ($wkType == 'proceedings') || ($wkType == 'magazine_article'))
		{
			if($this->endMonth)
			{
				$fields[] = 'miscField6';
				$values[] = $this->endMonth;
				$this->collectionStringArray['miscField6'] = $this->endMonth;
			}
			if($this->endDay)
			{
				$fields[] = 'miscField5';
				$values[] = $this->endDay;
				$this->collectionStringArray['miscField5'] = $this->endDay;
			}
		}
//		if(!isset($fields))
//			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$fields[] = 'addUserIdResource';
		$values[] = $this->session->getVar("setup_userId");
		$this->db->insert('WKX_resource_misc', $fields, $values);
	}
/*
* writeResourceYearTable - write WKX_resource_year table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
* @param $wkType - the WIKINDX resource type for this bibtex entry
*/
	function writeResourceYearTable($entry, $wkType)
	{
		foreach($entry as $bibField => $bibValue)
		{
			if($wkField = array_search($bibField, $this->map->{$wkType}['resource_year']))
			{
				$fields[] = $wkField;
				$values[] = $bibValue;
				$this->collectionStringArray[$wkField] = $bibValue;
			}
		}
		if(!isset($fields))
			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$this->db->insert('WKX_resource_year', $fields, $values);
	}
/*
* writeResourcePageTable - write WKX_resource_page table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
*/
	function writeResourcePageTable($entry)
	{
		if(!array_key_exists('pages', $entry))
			return;
		list($pageStart, $pageEnd) = $this->pages->init($entry['pages']);
		if($pageStart)
		{
			$fields[] = 'pageStart';
			$values[] = $pageStart;
		}
		if($pageEnd)
		{
			$fields[] = 'pageEnd';
			$values[] = $pageEnd;
		}
		if(!isset($fields))
			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$this->db->insert('WKX_resource_page', $fields, $values);
	}
/*
* writeResourceNoteTable - write WKX_resource_note table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
*/
	function writeResourceNoteTable($entry)
	{
		if(array_key_exists('note', $entry))
		{
			$fields[] = 'text';
			$values[] = $entry['note'];
		}
		else if(array_key_exists('annote', $entry))
		{
			$fields[] = 'text';
			$values[] = $entry['annote'];
		}
		if(!isset($fields))
			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$fields[] = 'addUserIdNote';
		$values[] = $this->session->getVar("setup_userId");
		$this->db->insert('WKX_resource_note', $fields, $values);
	}
/*
* writeResourceAbstractTable - write WKX_resource_abstract table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
*/
	function writeResourceAbstractTable($entry)
	{
		if(array_key_exists('abstract', $entry))
		{
			$fields[] = 'abstract';
			$values[] = $entry['abstract'];
		}
		if(!isset($fields))
			return;
		$fields[] = 'id';
		$values[] = $this->resourceId;
		$fields[] = 'addUserIdAbstract';
		$values[] = $this->session->getVar("setup_userId");
		$this->db->insert('WKX_resource_abstract', $fields, $values);
	}
/*
* writeResourceKeywordTable - write WKX_resource_keyword table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
*/
	function writeResourceKeywordTable($entry)
	{
		if(!array_key_exists('import_keywordSeparator', $this->vars))
			$separator = '1'; // default semicolon
		else
			$separator = $this->vars['import_keywordSeparator'];
		if(array_key_exists('keywords', $entry) && trim($entry['keywords']))
			$this->common->writeResourceKeywordTable($entry['keywords'], $this->resourceId, $separator);
	}
/*
* writeResourceCustomTable - write WKX_resource_custom table
* @author Mark Grimshaw
*
* @param $entry - assoc array of one entry for import.
*/
	function writeResourceCustomTable($custom)
	{
		if(empty($this->customFields))
			return;
		foreach($this->customFields as $importKey => $id)
		{
			if(!array_key_exists($importKey, $custom))
				continue;
			$fields = $values = array();
			$resultSet = $this->db->select(array('WKX_custom'), array('custom_size'), 
				" WHERE " . $this->db->formatField('id') . "=" . $this->db->tidyInput($id));
			$size = $this->db->fetchOne($resultSet);
			$size = $size == 'L' ? 'custom_long' : 'custom_short';
			$fields[] = "customId";
			$values[] = $id;
			$fields[] = "resourceId";
			$values[] = $this->resourceId;
			$fields[] = $size;
			$values[] = $custom[$importKey];
			$fields[] = 'addUserIdCustom';
			$values[] = $this->session->getVar("setup_userId");
			$this->db->insert('WKX_resource_custom', $fields, $values);
		}
	}
/*
* writeResourceCategoryTable - write WKX_resource_category table
* @author Mark Grimshaw
* @param $entry - assoc array of one entry for import.
*/
	function writeResourceCategoryTable()
	{
		if(!$groups = $this->session->getVar('import_groups'))
			$groups = 1; // force to 'General' category
		$this->db->insert('WKX_resource_category', array('id', 'categories'), 
			array($this->resourceId, $groups));
	}
/*
* writeImportRawTable - write WKX_import_raw table
* @author Mark Grimshaw
*/
	function writeImportRawTable()
	{
		if(empty($this->rejected) || !$this->session->getVar("import_raw"))
			return;
		$rejected = '';
		foreach($this->rejected as $key => $value)
			$rejected .= "$key = $value\n";
		$fields[] = 'id';
		$values[] = $this->resourceId;
		if(isset($this->bibtexStringId))
		{
			$fields[] = 'stringId';
			$values[] = $this->bibtexStringId;
		}
		$fields[] = 'text';
		$values[] = base64_encode(serialize($rejected));
		$fields[] = 'importType';
		$values[] = 'bibtex';
		$this->db->insert('WKX_import_raw', $fields, $values);
	}
/*
* writeTagTable - write import tag to WKX_tag table
* 
* @author Mark Grimshaw
* @return lastAutoId
*/
	function writeTagTable()
	{
		if($tagId = $this->session->getVar('import_tagId'))
			return $tagId;
		if(!$tag = $this->session->getVar('import_tag'))
			return FALSE;
		$this->db->insert('WKX_tag', array('tag'), 
			array($this->html->removeNl($tag)));
		return $this->db->lastAutoID('WKX_tag');
	}
/*
* writeBibtexStringTable - write $this->strings to WKX_bibtex_string table
* 
* @author Mark Grimshaw
*/
	function writeBibtexStringTable()
	{
		if(!empty($this->strings) && $this->session->getVar("import_raw"))
		{
			$fields[] = 'text';
			foreach($this->strings as $key => $value)
				$raw[] = '@STRING{' . $key . '=' . $value . '}';
			$values[] = base64_encode(serialize(implode("\n", $raw)));
			$this->db->insert('WKX_bibtex_string', $fields, $values);
			$this->bibtexStringId = $this->db->lastAutoId('WKX_bibtex_string');
		}
	}
/*
* writeResourceTimestampTable - write timestamp to WKX_resource_timestamp table
* 
* @author Mark Grimshaw
*/
	function writeResourceTimestampTable()
	{
		$this->db->insert('WKX_resource_timestamp', array('id', 'timestamp', 'timestampAdd'), 
			array($this->resourceId, $this->db->formatTimestamp(), $this->db->formatTimestamp()));
	}
/*
* grabHowPublished - check for type of howpublished field in bibtex misc entry
* 
* @author Mark Grimshaw
* @param $entry - assoc array of one entry for import.
* @return array(URL, howPublished)
*/
	function grabHowPublished($entry)
	{
		$url = $howPublished = FALSE;
		if(($entry['bibtexEntryType'] == 'misc') && array_key_exists('howpublished', $entry))
		{
			if(preg_match("#^\\\url{(.*://.*)}#", $entry['howpublished'], $match))
				$url = $match[1];
			else
				$howPublished = $entry['howpublished'];
		}
		return array($url, $howPublished);
	}
/*
* grabMonth - check for any month field and split into component day/month fields
* 
* @author Mark Grimshaw
* @param $entry - assoc array of one entry for import.
* @return array(startMonth, startDay, endMonth, endDay)
*/
	function grabMonth($entry)
	{
		$startMonth = $startDay = $endMonth = $endDay = FALSE;
		if(array_key_exists('month', $entry))
			list($startMonth, $startDay, $endMonth, $endDay) = $this->monthObj->init($entry['month']);
		return array($startMonth, $startDay, $endMonth, $endDay);
	}
// Write the bibtexKey field
	function writeBibtexKey()
	{
		$recordset = $this->db->select(array('WKX_resource'), 'bibtexKey');
		while($row = $this->db->loopRecordSet($recordset))
			$bibtexKeys[] = $row['bibtexKey'];
		$letters = range('a', 'z');
		$sizeof = sizeof($letters);
		$recordset = $this->db->select(array('WKX_resource_misc'), 
			array(array('WKX_resource_misc.id' => 'resourceId'), 'creator1', 'creator2', 'year1'), 
			" LEFT JOIN " . $this->db->formatTable('WKX_resource_creator') . ' ON ' . 
			$this->db->formatField('WKX_resource_misc.id') . '=' . 
			$this->db->formatField('WKX_resource_creator.id') . 
			" LEFT JOIN " . $this->db->formatTable('WKX_resource_year') . ' ON ' . 
			$this->db->formatField('WKX_resource_misc.id') . '=' . 
			$this->db->formatField('WKX_resource_year.id') . 
			' WHERE ' . $this->db->formatField('WKX_resource_misc.id') . "=" . $this->resourceId);
		$row = $this->db->fetchRow($recordset);
		$year = $row['year1'];
		if(!$row['creator1'] && $row['creator2']) // use editor
			$creatorId = array_shift(split(',', $row['creator2']));
		else
			$creatorId = array_shift(split(',', $row['creator1']));
		$recordset2 = $this->db->select(array('WKX_creator'), array('prefix', 'surname'), 
			' WHERE ' . $this->db->formatField('WKX_creator.id') . '=' . 
			$this->db->tidyInput($creatorId));
		$keyMade = FALSE;
		if(!$this->db->numRows($recordset2)) // anonymous
			$base = 'anon' . $year;
		else
		{
			$creator = $this->db->fetchRow($recordset2);
			$prefix = '';
			if($creator['prefix'])
			{
				$prefix = utf8_decode($creator['prefix']);
				foreach($this->config->bibtexSpChPlain as $key => $value)
				{
					$char = chr($key);
					$prefix = preg_replace("/$char/", "$value", $prefix);
				}
				$prefix = preg_replace("/\W/", '', $prefix);
			}
			$surname = utf8_decode($creator['surname']);
			foreach($this->config->bibtexSpChPlain as $key => $value)
			{
				$char = chr($key);
				$surname = preg_replace("/$char/", "$value", $surname);
			}
			$surname = preg_replace("/\W/", '', $surname);
			$base = $prefix . $surname . $year;
		}
		$bibtexKey = $base;
		for($i = 0; $i < $sizeof; $i++)
		{
			if(array_search($bibtexKey, $bibtexKeys) === FALSE)
			{
				$keyMade = TRUE;
				break;
			}
			$bibtexKey = $base . $letters[$i];
		}
		if(!$keyMade)
			$bibtexKey = $base . '.' . $this->resourceId; // last resort
		$this->db->updateSingle('WKX_resource', 
			$this->db->formatField('bibtexKey') .  "=" . $this->db->tidyInput($bibtexKey),
			" WHERE " . $this->db->formatField('id') . "=" . $this->resourceId);
	}
/*
* gatherStage1 - gather input from stage 1 and return a fullpath filename for parsing.
* If $this->paste is TRUE, this is a user cut 'n' pasting bibtex entries in a textarea box. We write the input to a 
* temporary file.
* 
* @author Mark Grimshaw
* @return string
*/
	function gatherStage1()
	{
// reset the session variable finalise flag
		$this->session->setVar('importLock', FALSE);
// bibtex_groups is a multiple select box so handle as array
		if(isset($this->vars['import_groups']) && $this->vars['import_groups'])
		{
			if($this->session->getVar("import_unrecognisedFields"))
				$this->vars['import_groups'] = explode(',', $this->vars['import_groups']);
			if(!$this->session->setVar('import_groups', trim(implode(',', $this->vars['import_groups']))))
				$this->badInput($this->errors->text("sessionError", "write"));
		}
// bib_ids is a multiple select box so handle as array
		if(isset($this->vars['import_bib_ids']) && $this->vars['import_bib_ids'])
		{
			if($this->session->getVar("import_unrecognisedFields"))
				$this->vars['import_bib_ids'] = explode(',', $this->vars['import_bib_ids']);
			if(!$this->session->setVar('import_bib_ids', trim(implode(',', $this->vars['import_bib_ids']))))
				$this->badInput($this->errors->text("sessionError", "write"));
		}
		if(isset($this->vars['import_raw']) && $this->vars['import_raw'])
		{
			if(!$this->session->setVar('import_raw', 1))
				$this->badInput($this->errors->text("sessionError", "write"));
		}
		if(!$this->session->setVar('import_keywordSeparator', $this->vars['import_keywordSeparator']))
			$this->badInput($this->errors->text("sessionError", "write"));
		if(!$this->session->setVar('import_titleSubtitleSeparator', $this->vars['import_titleSubtitleSeparator']))
			$this->badInput($this->errors->text("sessionError", "write"));
		if(isset($this->vars['import_importDuplicates']) && $this->vars['import_importDuplicates'])
		{
			if(!$this->session->setVar('import_importDuplicates', 1))
				$this->badInput($this->errors->text("sessionError", "write"));
		}
// Force to 1 => 'General' category
		if(!$this->session->getVar('import_groups'))
		{
			if(!$this->session->setVar('import_groups', 1))
				$this->badInput($this->errors->text("sessionError", "write"));
		}
		$dirName = "files/";
		if(!$this->paste && !$this->session->getVar("import_unrecognisedFields"))
		{
			if(!$this->importFile)
			{
				if(!isset($_FILES['import_file']))
				{
					if($file = $this->session->getVar('import_file'))
						return $dirName . $file;
					else
						$this->badInput($this->errors->text("file", "upload"));
				}
	// Check for file input
				$fileName = sha1(session_id() . $_FILES['import_file']['name']);
				if(!move_uploaded_file($_FILES['import_file']['tmp_name'], $dirName . $fileName))
					$this->badInput($this->errors->text("file", "upload"));
			}
			else // An import from a plug-in like ImportPubMed
			{
				$dirName = $this->vars['dirName'];
				$fileName = $this->importFile;
			}
			if(!$this->session->setVar('import_file', $fileName))
				$this->badInput($this->errors->text("sessionError", "write"));
			if($this->vars['import_tag'])
			{
				if(!$tagId = $this->tag->checkExists($this->vars['import_tag']))
				{
					if(!$this->session->setVar('import_tag', $this->vars['import_tag']))
						$this->badInput($this->errors->text("sessionError", "write"));
				}
				else
				{
					if(!$this->session->setVar('import_tagId', $tagId))
						$this->badInput($this->errors->text("sessionError", "write"));
				}
			}
			else if(isset($this->vars['import_tagId']) && $this->vars['import_tagId'])
			{
				if(!$this->session->setVar('import_tagId', $this->vars['import_tagId']))
					$this->badInput($this->errors->text("sessionError", "write"));
			}
			return $dirName . $fileName;
		}
		else if($this->paste)
		{
			if(!trim($this->vars['import_paste']))
				$this->badInput($this->errors->text("inputError", "missing"));
			include_once("core/file/FILE.php");
			$this->file = new FILE($this->db);
			$paste = stripslashes(trim($this->vars['import_paste']));
			list($fileName, $fullFileName) = $this->file->createFileName($paste, '.bib');
			if(!$fullFileName)
				$this->badInput($this->errors->text("file", "write", ": $fileName"));
			if(!$fp = fopen("$fullFileName", "w"))
				$this->badInput($this->errors->text("file", "write", ": $fileName"));
			if(!fputs($fp, $paste))
				$this->badInput($this->errors->text("file", "write", ": $fileName"));
			fclose($fp);
			return $fullFileName;
		}
	}
/*
* convertEntries - convert any laTeX code and convert to UTF-8 ready for storing in the database
* 
* @author Mark Grimshaw
* @param string $entries - multidimensional array of entries
* @return multidimensional array of converted entries.
*/
	function convertEntries($entries)
	{
		foreach($this->config->bibtexSpCh as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChOld as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChOld2 as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChLatex as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		$index = 0;
		foreach($entries as $array)
		{
			foreach($array as $key => $value)
				$temp[$index][$key] = preg_replace($matchBibtex, $replaceBibtex, $value);
			$index++;
		}
		return $temp;
	}
/*
* badInput - error handling - send user back to previous stage.
* 
* @author Mark Grimshaw
* @param string $error - any error message
*/
	function badInput($error)
	{
		include_once("core/template/TEMPLATE.php");
		$template = new TEMPLATE('content');
		include_once("core/file/import/BIBTEXIMPORTSTAGE1.php");
		$stage = new BIBTEXIMPORTSTAGE1($this->db, $this->vars);
		include_once("core/html/CLOSE.php");
		$template->setVar('heading', $this->messages->text("heading", "bibtexImport"));
// fix for user using browser back buttons - forces to the more innoccuous user paste.
		if(!isset($this->paste))
			$this->paste = TRUE;
		if(!$this->paste)
			$template->setVar('formStart', FORM::formMultiHeader("importBibtexStage2"));
		else
			$template->setVar('formStart', FORM::formHeader("resourcePasteBibtexStage2"));
		$template->setVar('body', $stage->init($this->paste, $error));
		$template->setVar('formEnd', FORM::formEnd());
		new CLOSE($this->db, $template->process());
	}
}
?>
